package cz.cuni.amis.episodic.lisp.visitor;

import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Deque;

import javax.naming.OperationNotSupportedException;

import lombok.Getter;

import cz.cuni.amis.episodic.lisp.behan.VisitableTree;
import cz.cuni.amis.episodic.lisp.netcreators.TraceModificationStrategy;

/**
 * 
 * @author ik
 */
public class HHMMmpTraceVisitor extends TreeActionsTraceVisitor {

	String[] header;

	@Getter
	List<Boolean[]> finish = new ArrayList<>();

	/**
	 * traces.get(i)[j] is i-th episode on depth j, higher level episodes have
	 * lower numbers
	 */
	@Getter
	List<String[]> traces = new ArrayList<>();

	@Override
	public boolean visitData(VisitableTree<ArrayList<String>> parent,
			ArrayList<String> data, boolean isLeaf) throws IOException {
		if (isLeaf) {
			// add new finish entry
			Boolean[] finishTrace = new Boolean[trace.size()];
			Arrays.fill(finishTrace, false);
			finish.add(finishTrace);
		}
		return super.visitData(parent, data, isLeaf);
	}

	@Override
	public boolean postVisitData(VisitableTree<ArrayList<String>> parent,
			ArrayList<String> data, boolean isLeaf) throws IOException {
		// correct last
		finish.get(finish.size() - 1)[trace.size() - 1] = true;
		return super.postVisitData(parent, data, isLeaf);
	}

	@Override
	void actionTraceVisited(List<String> actionsTrace) {
		traces.add(actionsTrace.toArray(new String[0]));
	}

	/**
	 * Discards higher levels of episode hierarchy.
	 * 
	 * @param maxLevel
	 */
	public void trimHighest(int maxLevel) {
		for (int i = 0; i < traces.size(); i++) {
			String[] trace = traces.get(i);
			if (maxLevel < trace.length - 1) {
				// we have to trim this trace
				String[] trimmedTrace = Arrays.copyOfRange(trace, trace.length
						- maxLevel - 1, trace.length);
				Boolean[] finishTrace = finish.get(i);
				Boolean[] trimmedFinish = Arrays.copyOfRange(finishTrace,
						finishTrace.length - maxLevel, finishTrace.length);

				traces.set(i, trimmedTrace);
				finish.set(i, trimmedFinish);
			}

		}
	}

	public void modify(TraceModificationStrategy strategy) {
		if (strategy != null) {
			traces = strategy.modifyTrace(traces, true);
			finish = strategy.modifyTrace(finish, false);
		}
	}

	/**
	 * Discards all but the N highest levels of hierarchy.
	 * 
	 * @param maxLevel
	 */
	public void trimLowest(int maxLevel) {
		// TODO
		throw new RuntimeException();
	}

	public void paddingUpper(int levelToPad) {
		for (int i = 0; i < traces.size(); i++) {
			String[] trace = traces.get(i);
			if (levelToPad > trace.length - 1) {
				// we have to pad this trace
				Boolean[] finishTrace = finish.get(i);

				String[] paddedTrace = new String[levelToPad + 1];
				Boolean[] paddedFinishTrace = new Boolean[levelToPad];
				int padds = paddedTrace.length - trace.length;
				// pad beginning
				for (int j = 0; j < padds; j++) {
					paddedTrace[j] = trace[0];
					paddedFinishTrace[j] = finishTrace[0];
				}
				// copy the rest
				for (int j = 0; j < paddedTrace.length - padds - 1; j++) {
					paddedTrace[padds + j] = trace[j];
					paddedFinishTrace[padds + j] = finishTrace[j];
				}
				paddedTrace[paddedTrace.length - 1] = trace[trace.length - 1];

				traces.set(i, paddedTrace);
				finish.set(i, paddedFinishTrace);
			}

		}
	}
}
